# see copyright and information at http://code.google.com/p/perl-ctx/
package Net::Coro::FTP;
use common::sense;
our $VERSION = 0.01;

use Carp;
use EV;
use Coro;
use Coro::EV;

use Net::Coro::EV::FTP;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(async);


use Net::Coro::Proxy;

*LOCALADDR = \$Net::Coro::EV::FTP::LOCALADDR;
*TIMEOUT = \$Net::Coro::EV::FTP::TIMEOUT;
*DEBUG = \$Net::Coro::EV::FTP::DEBUG;
#*REPEAT = todo;


sub new {
	my ($class, %prm) = @_;
	my ($socket_cmd, $socket_data);
	
	if (ref $prm{socket}) {
		$socket_cmd = delete $prm{socket_cmd} if ref $prm{socket_cmd};
		$socket_data = delete $prm{socket_data} if ref $prm{socket_data};
	} else {
		($socket_cmd, $socket_data) = Net::Coro::Proxy->ftp(delete($prm{socks5}), delete($prm{socks4}), delete($prm{proxy}));
	}
	delete($prm{http_proxy});
				
	my $this = {};
	bless $this, $class;
	$this->{_FTP} = Net::Coro::EV::FTP->new($socket_cmd, $socket_data);
	$this->{_QUIET} = delete ($prm{quiet}) || 0;
	
	$this->bindip(delete $prm{bindip}) if defined $prm{bindip};
	$this->timeout(delete $prm{timeout}) if defined $prm{timeout};
	$this->timeout_con(delete $prm{timeout_conn}) if defined $prm{timeout_conn};
	
	if (scalar keys %prm) {
		my @prm_names = keys %prm;
		die("unsupported parameter '$prm_names[0]'");
	}
	
	if ($Net::Coro::EV::HTTP::DEBUG) {
		my $coro = $Coro::current.'';
		my ($id) = $coro =~ /\((.*?)\)/s;
		$this->{_FTP}->id("#$id:".rand());
	}
	
	return $this;
}

$EV::DIED = sub {
	print "FATAL ERROR: $@\n";
	print "STACK TRACE: \n";
	foreach (1..5) {
		print join(' ',(caller($_))),"\n";
	}
	exit(1);
};


sub DESTROY {
	my ($this) = @_;
	$this->{_FTP}->stop if defined $this->{_FTP}; # check for global destructor
}
# if you want to break connection manually
sub stop {
	my ($this) = @_;
	$this->{_FTP}->stop;
	$this->{_ROUSE_CB}->($this->{_FTP}, undef, "stoped by user", 0) if defined $this->{_ROUSE_CB};
}
sub socket_cmd {$_[0]->{_FTP}->socket_cmd}
sub socket_data {$_[0]->{_FTP}->socket_data}

sub id {$_[0]->{_FTP}->id($_[1])}
sub bindip {$_[0]->{_FTP}->bindip($_[1])}
sub timeout {$_[0]->{_FTP}->timeout($_[1])}
sub timeout_conn {$_[0]->{_FTP}->timeout_conn($_[1])}

sub errc {$_[0]->{_FTP}->errc}
sub errm {$_[0]->{_FTP}->errm}

sub callback {
	my ($this, $ftp,  $code, $msg, $data) = @_; 

	delete $this->{_ROUSE_CB};
	if (!defined($code)) {
		if ($this->{_QUIET}) {
			$@ = $this->errm;
			return;
		}
		croak($this->errm);
	}
	
	return wantarray?($code, $msg, $data):$data;
}

### main methods ###
sub _rouse_cb {
	my ($this) = @_;
	$this->{_ROUSE_CB} = Coro::rouse_cb;
}

sub cmd {
	my ($this, $command) = @_;
	$this->{_FTP}->cmd($command, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}

sub connect {
	my ($this, $host) = @_;
	$this->{_FTP}->connect($host, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}
sub login {
	my ($this, $name, $pass) = @_;
	if (!defined($name)) {
		$this->{_FTP}->login($this->_rouse_cb);
	} else {
		$this->{_FTP}->login($name, $pass, $this->_rouse_cb);
	}
	$this->callback(Coro::rouse_wait);
}

sub binary {
	$_[0]->{_FTP}->binary($_[0]->_rouse_cb);
	$_[0]->callback(Coro::rouse_wait);
}
sub ascii {
	$_[0]->{_FTP}->ascii($_[0]->_rouse_cb);
	$_[0]->callback(Coro::rouse_wait);
}
sub pwd {
	$_[0]->{_FTP}->pwd($_[0]->_rouse_cb);
	$_[0]->callback(Coro::rouse_wait);
}

sub put {
	my ($this, $pathname, $filedata) = @_;
	$this->{_FTP}->put($pathname, $filedata, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}
sub upload {
	my ($this, $localfile, $remotefile) = @_;
	
	my $filedata;
	{
		local $/=undef;
		open FILE, "<$localfile" or die "FTP: can't read file $localfile: $!";
		binmode FILE;
		$filedata = <FILE>;
		close FILE;
	}
	
	$this->put($remotefile, $filedata);
}

# input parameters:
# - only remote $pathname 
# - remote $pathname and local $filename 
sub get {
	my ($this, $pathname, $filename) = @_;
	
	$this->{_FTP}->get($pathname, $this->_rouse_cb);
	my ($code, $msg, $data) = $this->callback(Coro::rouse_wait);
	return wantarray?($code, $msg, $data):$data if !$filename;
	return if $code !~ /^2/s;
	
	open FILE, ">$filename" or die "FTP: Couldn't write file >$filename: $!";
	binmode FILE;
	print FILE $data;
	close FILE;
	
	return wantarray?($code, $msg, $data):$data;
}

sub list {
	my ($this, $pathname) = @_;
	$pathname = '' if !defined($pathname);
	$this->{_FTP}->list($pathname, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}

sub nlst {
	my ($this, $pathname) = @_;
	$pathname = '' if !defined($pathname);
	$this->{_FTP}->nlst($pathname, $this->_rouse_cb);
	$this->callback(Coro::rouse_wait);
}


1;
